# 导入必要的库
import numpy as np  # 数值计算
import pandas as pd  # 数据处理
import warnings  # 忽略警告
import matplotlib.pyplot as plt  # 可视化
from sklearn.model_selection import train_test_split, KFold  # 数据集划分和交叉验证
from sklearn.metrics import mean_absolute_error  # 评估指标（MAE）
from sklearn.preprocessing import StandardScaler, PowerTransformer  # 数据标准化和分布变换
# from sklearn.feature_selection import mutual_info_regression # 导入但不再使用
import tensorflow as tf  # 深度学习框架
from tensorflow import keras  # Keras API
from tensorflow.keras import layers, callbacks  # 神经网络层和训练回调
from tensorflow.keras.regularizers import l2  # L2正则化

warnings.filterwarnings('ignore')  # 忽略警告信息

# 读取训练数据和测试数据
Train_data = pd.read_csv("C:/Users/86151/Desktop/深度学习课设/used_car_train_20200313.csv", sep=' ')
TestB_data = pd.read_csv("C:/Users/86151/Desktop/深度学习课设/used_car_testB_20200421.csv", sep=' ')

# 选择数值型特征列（排除非数值列和无关列）
numberical_cols = Train_data.select_dtypes(exclude='object').columns
feature_cols = [col for col in numberical_cols if
                col not in ['SaleID', 'name', 'regDate', 'price', 'model', 'brand', 'regionCode']]
# 打印一下你选择的特征，确认是否正确
print("Selected features:", feature_cols)

# --- 提取特征和标签 ---
# 使用手动选择的特征列
X_data = Train_data[feature_cols]  # 训练集特征
Y_data = Train_data['price']  # 训练集标签（二手车价格）
X_test = TestB_data[feature_cols]  # 测试集特征

# --- 预处理函数 ---
def enhanced_preprocessing(X, y=None):
    # 填充缺失值（使用中位数）
    X = X.fillna(X.median())
    # 处理异常值（基于IQR方法）
    for col in X.columns:
        q1 = X[col].quantile(0.25)
        q3 = X[col].quantile(0.75)
        iqr = q3 - q1
        lower_bound = q1 - 1.5 * iqr
        upper_bound = q3 + 1.5 * iqr
        X[col] = X[col].clip(lower_bound, upper_bound)# 将超出范围的值截断

    # 使用PowerTransformer（Yeo-Johnson方法）处理偏态分布
    pt = PowerTransformer(method='yeo-johnson')
    X_transformed = pt.fit_transform(X)
    X = pd.DataFrame(X_transformed, columns=X.columns)

    # 标准化（均值为0，方差为1）
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    X = pd.DataFrame(X_scaled, columns=X.columns)
    return X

# --- 标签变换（对数变换，使分布更接近正态）---
Y_data_transformed = np.log1p(Y_data)

# --- 应用预处理 ---
# 这里不再进行互信息特征选择，直接使用 feature_cols
X_data_processed = enhanced_preprocessing(X_data)  # 预处理训练集特征
X_test_processed = enhanced_preprocessing(X_test)  # 预处理测试集特征

# 打印预处理后的形状，确认是否一致
print("X_data_processed shape:", X_data_processed.shape)
print("X_test_processed shape:", X_test_processed.shape)

# --- 划分数据集 ---
x_train, x_val, y_train, y_val = train_test_split(
    X_data_processed,
    Y_data_transformed,
    test_size=0.2,  # 20%作为验证集
    random_state=42  # 固定随机种子确保可复现性
)

# --- 构建增强的DNN模型 ---
def build_enhanced_dnn(input_shape):
    model = keras.Sequential([
        # 第一层：256个神经元，Swish激活，L2正则化，BatchNorm和Dropout
        layers.Dense(256, activation='swish', input_shape=[input_shape],
                     kernel_regularizer=l2(0.001)),
        layers.BatchNormalization(),
        layers.Dropout(0.4),

        # 第二层：128个神经元，Swish激活，L2正则化，BatchNorm和Dropout
        layers.Dense(128, activation='swish', kernel_regularizer=l2(0.001)),
        layers.BatchNormalization(),
        layers.Dropout(0.3),

        # 第三层：64个神经元，Swish激活，L2正则化，BatchNorm和Dropout
        layers.Dense(64, activation='swish', kernel_regularizer=l2(0.001)),
        layers.BatchNormalization(),
        layers.Dropout(0.2),

        # 第四层：32个神经元，Swish激活
        layers.Dense(32, activation='swish'),
        # 输出层：1个神经元（回归问题）
        layers.Dense(1)
    ])

    # 使用Adam优化器，学习率0.0005
    optimizer = tf.keras.optimizers.Adam(learning_rate=0.0005)
    model.compile(loss='mae', optimizer=optimizer, metrics=['mae'])  # 损失函数为MAE
    return model

# --- 训练回调（早停和学习率调度）---
early_stopping = callbacks.EarlyStopping(
    patience=20,  # 20轮无改善后停止
    min_delta=0.001,  # 最小变化阈值
    restore_best_weights=True,  # 恢复最佳权重
)

lr_scheduler = callbacks.ReduceLROnPlateau(
    patience=5,  # 5轮无改善后降低学习率
    factor=0.5,  # 学习率减半
    min_lr=1e-6  # 最小学习率
)

# --- 可视化函数 ---
def plot_history(history):
    plt.figure(figsize=(12, 4))
    plt.subplot(1, 2, 1)
    plt.plot(history.history['loss'], label='Train Loss')
    plt.plot(history.history['val_loss'], label='Validation Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()

    plt.subplot(1, 2, 2)
    plt.plot(history.history['mae'], label='Train MAE')
    plt.plot(history.history['val_mae'], label='Validation MAE')
    plt.xlabel('Epochs')
    plt.ylabel('MAE')
    plt.legend()
    plt.show()

# --- 训练完整模型并可视化 ---
input_shape = X_data_processed.shape[1]  # 输入特征维度
full_model = build_enhanced_dnn(input_shape)  # 构建模型

# 训练模型
history = full_model.fit(
    X_data_processed, Y_data_transformed,
    validation_split=0.2,  # 验证集比例
    epochs=100,  # 训练轮数
    batch_size=64,  # 批大小
    verbose=1,  # 显示进度
    callbacks=[early_stopping, lr_scheduler]  # 回调函数
)
# 绘制损失和MAE曲线
plot_history(history)

# --- 预测和保存结果 ---
# 使用刚刚训练好的 full_model 进行预测
test_preds = full_model.predict(X_test_processed).flatten()

# 对预测结果进行逆变换
test_preds_price = np.expm1(test_preds)

# 保存结果
sub = pd.DataFrame()
sub['SaleID'] = TestB_data['SaleID']
sub['price'] = test_preds_price
sub.to_csv("C:/Users/86151/Desktop/深度学习课设/sub_enhanced_dnn.csv", index=False)
print("预测结果已保存到 sub_enhanced_dnn.csv")


